﻿namespace NJQuery {
	using System;
	using System.Drawing;
	using System.Web;
	using System.Web.UI;
	using System.Text;
	using System.Collections.Generic;
	using System.Globalization;
	using System.Diagnostics.CodeAnalysis;

	/// <summary>
	/// Provides extended instance methods for objects that cannot otherwise have them defined.
	/// </summary>
	public static class Extensions {


		#region Static Methods: Enumeration instance methods

		/// <summary>
		/// Returns the CSS class associated with a panel type defined by the <see cref="PanelType" /> enumeration.
		/// </summary>
		/// <param name="panelType">The <see cref="PanelType" /> enumeration value being extended.</param>
		/// <returns>The associated CSS class.</returns>
		public static string GetCssClass(this PanelType panelType) {
			return PanelTypeAttribute.GetCssClass(panelType);
		}

		/// <summary>
		/// Returns the CSS class associated with a priority defined by the <see cref="Priority" /> enumeration.
		/// </summary>
		/// <param name="priority">The <see cref="Priority" /> enumeration value being extended.</param>
		/// <returns>The associated CSS class.</returns>
		public static string GetCssClass(this Priority priority) {
			return PriorityAttribute.GetCssClass(priority);
		}

		/// <summary>
		/// Returns a jQuery selector for a given <see cref="Accordion" /> header tag.
		/// </summary>
		/// <param name="headerTag">The <see cref="AccordionHeaderTag" /> enumeration value being extended.</param>
		/// <returns>The appropriate jQuery selector.</returns>
		public static string GetSelector(this AccordionHeaderTag headerTag) {
			if(headerTag == AccordionHeaderTag.Div) return "> div:even";
			return string.Format(CultureInfo.InvariantCulture, "> {0}", ScriptBuilder.GetCamelCaseValue(headerTag));
		}

		#endregion


		#region Static Methods: ToClientSource() methods

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="AccordionHeaderTag"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this AccordionHeaderTag value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="AccordionStandardAnimation"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "The ToLowerInvariant() is being used to lower-case the value, since that is what the client needs and not to compare strings.")]
		public static string ToClientSource(this AccordionStandardAnimation value) {
			if(value == AccordionStandardAnimation.False) return false.ToClientSource();
			//These properties use all lower case rather than camel casing
			return value.ToString().ToLowerInvariant().ToClientSource();
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="BlindDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this BlindDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="BlindMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this BlindMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="BounceDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this BounceDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="BounceMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this BounceMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="ClipDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this ClipDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="ClipMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this ClipMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="DropDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this DropDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="DropMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this DropMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="DatePickerStandardAnimation"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this DatePickerStandardAnimation value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="DropDownShowOn"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this DropDownShowOn value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="FoldMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this FoldMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="HighlightMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this HighlightMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="HorizontalAlign"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this HorizontalAlign value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="PanelActivateEvent"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this PanelActivateEvent value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="PuffMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this PuffMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="PulsateMode"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this PulsateMode value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="ScaleDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this ScaleDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="ScaleType"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this ScaleType value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="ShakeDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this ShakeDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="SlideDirection"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this SlideDirection value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="SliderFixedRange"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this SliderFixedRange value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="SliderOrientation"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this SliderOrientation value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Speed"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this Speed value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="VerticalAlign"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this VerticalAlign value) {
			return GetEnumSource(value);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Color"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Not doing a comparison.  Just need invariant lower case.")]
		public static string ToClientSource(this Color value) {
			return ColorTranslator.ToHtml(value).ToLowerInvariant().ToClientSource();
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Boolean"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this bool value) {
			return value ? "true" : "false";
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="Boolean"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this bool[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Char"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this char value) {
			return value.ToString(CultureInfo.InvariantCulture).ToClientSource();
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="DateTime"/> value being extended.</param>
		/// <param name="includeTime">Whether or not to include the time portion along with the date.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this DateTime value, bool includeTime) {
			return ScriptBuilder.GetCreateDateTimeScript(value, includeTime);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="DateTime"/> values being extended.</param>
		/// <param name="includeTime">Whether or not to include the time portion along with the date.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this DateTime[] values, bool includeTime) {
			return GetArraySource(values, value => ToClientSource(value, includeTime));
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Decimal"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this decimal value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="Decimal"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this decimal[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Double"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this double value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="Double"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this double[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Single"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this float value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="Single"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this float[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Int32"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this int value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="Int32"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this int[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="Int64"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this long value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="Int64"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this long[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="String"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this string value) {
			return ToClientSource(value, false);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="String"/> value being extended.</param>
		/// <param name="htmlEncode">Whether or not to HTML encode the value in addition to JavaScript encoding.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this string value, bool htmlEncode) {
			if(htmlEncode) value = HttpUtility.HtmlEncode(value);
			return string.Format(CultureInfo.InvariantCulture, "'{0}'", ScriptBuilder.JavaScriptStringEncode(value));
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="String"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this string[] values) {
			return ToClientSource(values, false);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="String"/> values being extended.</param>
		/// <param name="htmlEncode">Whether or not to HTML encode the value in addition to JavaScript encoding.</param>
		/// <returns>The client script value.</returns>
		public static string ToClientSource(this string[] values, bool htmlEncode) {
			return GetArraySource(values, value => ToClientSource(value, htmlEncode));
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="UInt32"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		[CLSCompliant(false)]
		public static string ToClientSource(this uint value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="UInt32"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		[CLSCompliant(false)]
		public static string ToClientSource(this uint[] values) {
			return GetArraySource(values, ToClientSource);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="value">The <see cref="UInt64"/> value being extended.</param>
		/// <returns>The client script value.</returns>
		[CLSCompliant(false)]
		public static string ToClientSource(this ulong value) {
			return value.ToString(CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Returns a literal JavaScript value used to represent the server value on the client.
		/// </summary>
		/// <param name="values">The array of <see cref="UInt64"/> values being extended.</param>
		/// <returns>The client script value.</returns>
		[CLSCompliant(false)]
		public static string ToClientSource(this ulong[] values) {
			return GetArraySource(values, ToClientSource);
		}

		private static string GetEnumSource<T>(T value) where T : struct {
			return ScriptBuilder.GetCamelCaseValue(value).ToClientSource();
		}

		private delegate string ToClientSourceDelegate<T>(T value);
		private static string GetArraySource<T>(T[] values, ToClientSourceDelegate<T> ToClientSource) {
			StringBuilder source = new StringBuilder();

			source.Append("[");

			bool isFirst = true;
			foreach(T value in values) {

				if(isFirst) isFirst = false;
				else source.Append(",");

				source.Append(ToClientSource(value));
			}

			source.Append("]");

			return source.ToString();
		}

		#endregion


		#region Static Methods: Control instance methods

		internal static string GetClientId(this Control control, string serverId) {

			if(string.IsNullOrEmpty(serverId)) return null;

			Control targetControl = control.NamingContainer.FindControl(serverId);
			if(targetControl == null) return null;
			return targetControl.ClientID;
		}

		#endregion


	}

}